package com.bruce.storage.service;

import com.bruce.storage.config.StorageConfig;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "minio")
public class MinIOStorageService implements StorageService {
    private final MinioClient minioClient;

    public MinIOStorageService(StorageConfig properties) {
        this.minioClient = MinioClient.builder()
                .endpoint(properties.getEndpoint())
                .credentials(properties.getAccessKey(), properties.getSecretKey())
                .build();
    }

    @Override
    public void uploadFile(String bucketName, String contentId, String fileName,String referenceIds, MultipartFile file) {
        Map<String, String> metadata = new HashMap<>();
        metadata.put("name", fileName);
        metadata.put("reference_ids", referenceIds != null ? referenceIds : "");
        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(contentId)
                            .userMetadata(metadata)
                            .stream(file.getInputStream(), file.getSize(), -1) // partSize -1 means auto calculate part size
                            .build());
        } catch (Exception e) {
            throw new RuntimeException("Failed to upload file to MinIO", e);
        }
    }

    @Override
    public void uploadFile(String bucketName, String contentId, String fileName, String referenceIds, InputStream inputStream) {
        Map<String, String> metadata = new HashMap<>();
        metadata.put("name", fileName);
        metadata.put("reference_ids", referenceIds != null ? referenceIds : "");
        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(contentId)
                            .userMetadata(metadata)
                            .stream(inputStream, inputStream.available(), -1) // partSize -1 means auto calculate part size
                            .build());
        } catch (Exception e) {
            throw new RuntimeException("Failed to upload file to MinIO", e);
        }
    }

    @Override
    public InputStream downloadFile(String bucketName, String contentId) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(contentId)
                            .build());

        } catch (Exception e) {
            log.error("Failed to download file from MinIO", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteFile(String bucketName, String fileName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(fileName)
                            .build());
        } catch (Exception e) {
            log.error("Failed to delete file from MinIO", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public void deleteFiles(String bucketName, List<String> fileNames) {
        List<DeleteObject> deleteObjects = fileNames.stream().map(DeleteObject::new).collect(Collectors.toList());
        try {
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(
                    RemoveObjectsArgs.builder()
                            .bucket(bucketName)
                            .objects(deleteObjects)
                            .build());
            for (Result<DeleteError> result : results) {
                DeleteError error = result.get();
                log.info("Error in deleting object {} : {}", error.objectName(), error.message());
            }
        } catch (Exception e) {
            log.error("Failed to delete files from MinIO", e);
            return;
        }
        log.info("delete files result: {}", fileNames);
    }

    @Override
    public Map<String, String> getMetadata(String bucketName, String contentId) {
        try {
            return minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(contentId)
                            .build()).userMetadata();
        } catch (Exception e) {
            log.error("Failed to get metadata from MinIO", e);
            return null;
        }
    }
}
